// js 代码执行过程分为两个阶段
// 1词法分析：词法分析主要包括：分析变量声明、分析函数声明、分析形参三个部分。
// 2执行阶段
// https://www.jianshu.com/p/83123eb30237




////变量提升
{
    console.log(a); //undefined
    var a = 123;
    // 引擎通过词法分析将我们写的js代码转成可以执行的代码，接下来才是执行。

    var a;
    console.log(a)
    a = 123
    //所以输出内容为 undeifend


    {
        console.log(v1);
        var v1 = 100;

        function foo() {
            console.log(v1);
            var v1 = 200;
            console.log(v1);
        }
        foo();
        console.log(v1);
        // undefined  undefined 200  100
    }
}


////函数提升
//具名函数的声明有两种方式：1. 函数声明式 (等级最高)2. 函数字面量式
{
    //函数声明式
    function bar() {}
    //变量形式声明； 
    var foo = function () {}

    //函数 变量形式声明 和普通变量一样 提升的 只是一个没有值的变量。

    //函数声明式的提升现象和变量提升略有不同， 函数声明式会提升到作用域最前边， 并且将声明内容一起提升到最上边。


    {
        bar()

        var bar = function () {
            console.log(1);
        }
        // 报错：TypeError: bar is not a function
    } {
        bar()

        function bar() {
            console.log(1);
        }
        //输出结果1
    }
    // 知识点总结
    //  所有的声明都会提升到作用域的最顶上去。

    // 同一个变量只会声明一次， 其他的会被忽略掉或者覆盖掉。

    // 函数声明的优先级高于变量声明的优先级， 并且函数声明和函数定义的部分一起被提升。

} {
    //函数的优先级更高，所以变量的值会被函数覆盖掉。
    {
        var myName;

        function myName() {
            console.log("Rich");
        }
        console.log(typeof myName); // function
    } {
        var myName = "Richard";

        function myName() {
            console.log("Rich");
        }

        console.log(typeof myName); // string

        //因为上面的代码等价于
        var myName;

        function myName() {
            console.log("Rich");
        }
        //上面是提升的区域
        myName = "Richard"; //然后再赋值
        console.log(typeof myName); // string
    }
}

{
    var a = 1;

    function b() {
        //function a() {} 提升 a 赋值给 function a()
        a = 10;
        console.log(a)//10
        return;

        function a() {}
    }
    b();
    alert(a); //1
}

//// 例子
{
    //同一个变量只会声明一次， 其他的会被忽略掉或者覆盖掉。
    var x = 1;
    console.log(x); // 1
    if (true) {
        var x = 2;
        console.log(x); // 2
    }
    console.log(x); // 2
}

{
    var x = 0;
    console.log(x) //0

    function foo() {
        var x = 1;
        console.log(x) //1
        if (x) {
            console.log(x); //1
            (function () {
                console.log(x) //undefined
                var x = 2;
                console.log(x) //2
            }());
        }
        console.log(x) //1
    }
    foo()
    console.log(x) //0

    //上面代码在if条件块中创建了一个闭包，它是一个立即执行函数，所以相当于我们又创建了一个函数作用域，所以内部的x并不会对外部产生影响。
}

{
    //如果声明一个变量的时候没有使用var关键字，那么这个变量将是一个全局变量！  局部变量优先级大于全局变量
    {
        var name = "Michael Jackson";

        function showCelebrityName() {
            console.log(name);
        }

        function showOrdinaryPersonName() {
            name = "Johnny Evers"; //没有var 覆盖全局变量
            console.log(name);
        }
        showCelebrityName(); // Michael Jackson

        showOrdinaryPersonName(); // Johnny Evers


        showCelebrityName(); // Johnny Evers
    }

    {
        var name = "Michael Jackson";

        function showCelebrityName() {
            console.log(name);
        }

        function showOrdinaryPersonName() {
            name = "Johnny Evers";
            console.log(name);
        }
        showCelebrityName(); // Michael Jackson

        showOrdinaryPersonName(); // Johnny Evers


        showCelebrityName(); // Michael Jackson
        function showOrdinaryPersonName() {
            var name = "Johnny Evers"; //有var 不覆盖
            console.log(name);
        }
    }

}


function add(x, y) {
    return x + y;
}

// 将函数作为参数和返回值
function a(op) {
    return op;
}
console.log(a(add)(1, 1))




{
    // let 的「创建」过程被提升了，但是初始化没有提升。
    // var 的「创建」和「初始化」都被提升了。
    // function 的「创建」「初始化」和「赋值」都被提升了。
    // const，其实 const 和 let 只有一个区别，那就是 const 只有「创建」和「初始化」，没有「赋值」过程。

    console.log(x) // Uncaught ReferenceError: x is not defined
    let x = 1

    console.log(x) // undefined
    var x = 1
}